import { parseString } from 'xml2js';
import moment from 'moment';
import {
    getWxAccessToken,
    getWechatIdBySn,
    getWechatPublicPlatformBySn,
    getWechatPublicPlatformById,
} from '../controllers/wx_token';

import {
    getOpenId,
    sendToWXUser,
    sendMessageToOpenid,
    sendNewMemberMessage
} from './wx_handle';
import { control } from '../controllers/baidu_api';
import { exesql, insertTable } from '../database/db';
import { sendNewVoice,sendSubscribeMessage } from '../database/redis_db';
import { sendLogToEmail } from '../controllers/logger';
import {WX_MESSAGE_VALID_TIME} from '../config/config';
import TaskQueue from '../biz/task_queue';

class Wx {
    constructor() {
        this.handleWxPost = this.handleWxPost.bind(this);
        this.handleWxGet = this.handleWxGet.bind(this);
        this.getOpenid = this.getOpenid.bind(this);

        // 发送语音给绑定用户队列
        this.voiceQueue = new TaskQueue(3);
    }

    // 来自微信的Get请求
    async handleWxGet(req, res) {
        res.end(req.query.echostr)
    }

    async handleWxPost(req, res) {
        res.end('');
        try {
            const { err, result } = await this.decodeWxXmlData(req.body);
            if (err) {
                sendLogToEmail(err);
                return;
            }
            // 根据解析的数据类型做区分处理
            console.log('Wx Message >>>> ', result);
            if (result.xml.MsgType[0] === 'voice') {
                // 语音消息
                const openid = result.xml.FromUserName[0];
                let txt = result.xml.Recognition[0];
                if (txt.length > 0) {
                    txt = txt.substring(0, txt.length - 1);
                    this.insertFeedback(openid, txt,1);
                }
                this.handleVoice({ fromUserName: openid, media_id: result.xml.MediaId[0], toUserName: result.xml.ToUserName[0] });

            } else if (result.xml.Event && result.xml.Event[0] === 'subscribe') {
                // 订阅
                let eventKeyValue = '';
                if (result.xml.EventKey && result.xml.EventKey[0]) {
                    eventKeyValue = result.xml.EventKey[0];
                }
                this.handleSubscribe(eventKeyValue, result.xml.FromUserName[0], result.xml.ToUserName[0]);

            } else if (result.xml.Event && result.xml.Event[0] === 'SCAN') {
                // 扫描
                const device_sn = result.xml.EventKey[0];
                const fromUserName = result.xml.FromUserName[0];
                if (device_sn) {
                    console.log(`触发绑定接口 from:${fromUserName}, device_sn:${device_sn}`)
                    this.subscribeBindDevice(device_sn, fromUserName);
                }
            }else if (result.xml.MsgType[0] === 'text') {
                // 用户反馈信息
                const openid = result.xml.FromUserName[0];
                const txt = result.xml.Content[0];
                this.insertFeedback(openid, txt,0);
            }else if (result.xml.MsgType[0] === 'event' && result.xml.EventKey[0] === 'RECEIVE_MESSAGE') {
                const toUserName = result.xml.ToUserName[0];
                const fromUserName = result.xml.FromUserName[0];
                console.log(`toUserName:${toUserName}, fromUserName:${fromUserName}`);
                this.pushMessageToUser(toUserName, fromUserName);
            }
        } catch (err) {
            sendLogToEmail(err);
        }
    }

    async insertFeedback(openid, text,type) {
        await insertTable({
            open_id: openid,
            text: text,
            type: type,
        }, 'wx_feedback');
    }


    decodeWxXmlData(xml) {
        return new Promise(function (resolve, reject) {
            parseString(xml, function (err, result) {
                resolve({ err: err, result: result });
            })
        });
    }

    async getOpenid(req, res) {
        const wxCode = req.query.code;
        const ticket = req.query.ticket;
        const url = req.query.url;
        if (!wxCode || !url) {
            res.end(JSON.stringify({ errcode: -1 }));
            return;
        }
        const arry = url.split('?');
        const wechat_id = arry[1].split('=')[1];
        console.log(`>>>>>>> wxCode:${wxCode}, redirectUrl;${arry[0]}, wechat_id:${wechat_id},ticket:${ticket}`);
        const config = getWechatPublicPlatformById(wechat_id);
        try {
            const { err, result } = await getOpenId(config.app_id, config.app_secret, wxCode);
            if (err) {
                console.log('获取openid失败:', err.stack);
                sendLogToEmail(err);
                return;
            }
            const json = JSON.parse(result);
            const msg = "******************" + json.openid + " ***************" + arry[0];
            console.log(msg);
            if (!json.openid) {
                sendLogToEmail(msg);
            }
            // 保存到cookie中
            res.cookie('openid_' + wechat_id, json.openid || '');
            res.cookie('wechat_id', wechat_id);
            if (ticket) {
                res.redirect(arry[0] + '/:' + ticket);
            } else {
                res.redirect(arry[0]);
            }
        } catch (err) {
            sendLogToEmail(err);
            res.end(JSON.stringify({ errcode: -1 }));
        }

    }

    //*******微信服务器推送信息处理*********** */

    async handleVoice(media) {
        const { media_id, fromUserName, toUserName } = media;
        console.log(media_id, fromUserName, toUserName);
        try {
            // 根据openid查询出对应的绑定设备
            const sql = 'SELECT open_id,device_sn FROM device_openid  WHERE device_sn IN (SELECT device_sn FROM device_openid  WHERE open_id = ?)';
            const { err, result } = await exesql(sql, [fromUserName]);
            if (err) {
                sendLogToEmail(err);
                return;
            }
            if (result.length === 0) {
                console.log(`handleVoice 根据:${fromUserName} 查询不到用户设备`);
                return;
            }
            const deviceSn = result[0].device_sn;
            // 通知设备
            this.notifyDevice(deviceSn, media_id);
            // 获取发送微信消息的token
            const wechatId = getWechatIdBySn(toUserName);
            const accessToken = await this.getWxToken(wechatId);
            if (accessToken === '') {
                sendLogToEmail(`handleVoice wechatId:${wechatId}, 获取不到token`);
                return;
            }

            for (let index = 0; index < result.length; index++) {
                const item = result[index];
                if (fromUserName !== item.open_id) {
                    sendNewVoice(accessToken, deviceSn,item.open_id,media_id,0);
                    // this.voiceQueue.pushTask(done => {
                    //     sendToWXUser(accessToken, item.open_id, media_id).then((mResult) => {
                    //         done();
                    //         if (mResult.result && mResult.result.errcode === 45047) {
                    //             // 存储设备发送的消息作为离线消息,用户拉取最新消息后发送离线消息
                    //             insertTable({device_sn:deviceSn,media_id:media_id,open_id:item.open_id,status:0},'wx_offline_voice').then(function(item){
                    //                 if (item.err) {
                    //                     console.log(">>>>>>>>",item.err);
                    //                 }
                    //             });
                    //         }
                    //     });
                    // })
                }
            }

        } catch (err) {
            sendLogToEmail(err);
        }

    }


    //通过百度接口发送指令
    async notifyDevice(device_sn, media_id) {
        const sql = 'SELECT `uuid`,`token` FROM `device` WHERE sn = ? LIMIT 1;'
        try {
            const { err, result } = await exesql(sql, [device_sn]);
            if (err || result.length === 0) {
                sendLogToEmail(`notify err:${err.stack} \n,result:${JSON.stringify(result)}`);
                this.addWxVoice(device_sn, media_id, 0);
                return;
            }

            // 通过百度接口通知设备
            const data = await control(result[0].uuid, {type: 5, media_id: media_id}, result[0].token);
            if (data.err) {
                sendLogToEmail(data.err);
                this.addWxVoice(device_sn, media_id, 0);
                return;
            }
            // 插入语音
            this.addWxVoice(device_sn, media_id, parseInt(data.result.code, 10) === 0 ? 1:0);
        } catch (err) {
            sendLogToEmail(err);
        }
    }

    addWxVoice(device_sn, media_id,notify_status){
        return insertTable({
            device_sn: device_sn,
            media_id: media_id,
            notify_status:notify_status
        }, 'wx_voice');
    }

    // 处理订阅事件(目前用于两个方面:关注发送绑定设备信息,通过扫毛进入关注界面触发绑定事件(eventKeyValue 有值))
    async handleSubscribe(eventKeyValue, open_id, deviceType) {
        console.log(`eventKeyValue:${eventKeyValue}`);
        if (eventKeyValue) {
            const arr = eventKeyValue.split('_');
            const device_sn = arr[1] + "_" + arr[2] + "_" + arr[3];
            console.log('触发绑定接口 from:' + open_id + ' to:' + deviceType + ", device_sn:" + device_sn);
            this.subscribeBindDevice(device_sn, open_id);

        } else {
            console.log('用户关注公众号 from:' + open_id + ' to:' + deviceType);
            const config = getWechatPublicPlatformBySn(deviceType)
            try {
                const accessToken = await this.getWxToken(config.wechat_id);
                if (accessToken === '') {
                    sendLogToEmail(`handleVoice open_id:${open_id}, 获取不到token`);
                    return;
                }
                sendSubscribeMessage(accessToken, open_id,config.host,config.wechat_id,config.bind_img_name);
                // sendBindDeviceNotification(accessToken, open_id, config);
            } catch (error) {
                sendLogToEmail(error);
            }
        }
    }

    // 关注并且绑定设备
    async subscribeBindDevice(deviceSn, openId) {
        // FIXME:加入事务处理
        const sql = 'SELECT `token` FROM `device` WHERE `sn` = ? LIMIT 1';
        try {
            const { err, result } = await exesql(sql, [deviceSn]);
            if (err) {
                sendLogToEmail(err);
                return;
            }
            if (result.length === 0) {
                console.log(` ${deviceSn} 当前设备不存在...`);
            }

            const delSql = 'DELETE FROM `device_openid` WHERE `open_id` = ?';
            const deletResult = await exesql(delSql, [openId]);
            if (deletResult.err) {
                sendLogToEmail(deletResult.err);
                return;
            }
            const insertSql = 'INSERT INTO `device_openid` (`device_sn`,`open_id`) VALUES (?,?);';
            const insertResult = await exesql(insertSql, [deviceSn, openId]);
            if (insertResult.err) {
                sendLogToEmail(insertResult.err);
                return;
            }
            // 消息通知加入家庭圈
            const wechatId = getWechatIdBySn(deviceSn);
            const wxToken = await this.getWxToken(wechatId);
            sendNewMemberMessage(wxToken, deviceSn, openId);
        } catch (err) {
            sendLogToEmail(err);
        }
    }

    // 推送消息给用户
    async pushMessageToUser(deviceType,openid){

        try {
            const {err,result} = await exesql('SELECT device_openid.device_sn FROM device_openid WHERE device_openid.open_id = ? LIMIT 1;',[openid]);
            if (err) {
                sendLogToEmail(err);
                return;
            }

            const wechatId = getWechatIdBySn(deviceType);
            if (wechatId === 0) {
                sendLogToEmail(`openid:${openid},无法获取到wechatid`);
                return;
            }
            const wxToken = await this.getWxToken(wechatId);
            if (result.length === 0) {
                sendMessageToOpenid(wxToken, openid, '您还未绑定设备');
                return;
            }
            const device_sn = result[0].device_sn;
            const date = new Date((new Date().getTime() - WX_MESSAGE_VALID_TIME));
            const beforeThirdDay = moment(date).format('YYYY-MM-DD HH:mm:ss.SSS');
            const sql = 'SELECT `id`,`media_id` FROM `wx_offline_voice` WHERE `device_sn` = ? and `status` = 0 AND `create_at` > ? AND `open_id` = ? order by `id` ASC LIMIT 19;';
            const mResult = await exesql(sql, [device_sn, beforeThirdDay,openid]);
            if (mResult.err) {
                console.log(mResult.err);
                return;
            }
            if (mResult.result.length === 0) {
                sendMessageToOpenid(wxToken, openid, '您没有未读语音消息');
                return;
            }

            // let idList = [];
            for (let index = 0; index < mResult.result.length; index++) {
                const voice = mResult.result[index];
                sendNewVoice(wxToken,device_sn,openid,voice.media_id,1);
                // const {err} = await sendToWXUser(wxToken, openid,voice.media_id);
                // if (!err) {
                //     idList.push(voice.id);
                // }
                // await this.sleep(200);
            }
            // if(idList.length > 0){
            //     this.updateOfflineVoices(idList);
            // }
        
        } catch (error) {
            console.log(`推送消息异常:`, error);
            sendLogToEmail(error);
        }
    }

    /**
     * 更新离线消息状态
     * @param {更新语音状态的id列表} idList 
     */
    updateOfflineVoices(idList){
        const idString = idList.join(',');
        const sql = 'UPDATE `wx_offline_voice` SET `status`= 1 WHERE `id` IN (' + idString + ');';
        exesql(sql,[]).then(function(result){
            if (result.err) {
                sendLogToEmail(result.err);
            }
        })
    }


    getWxToken(wechat_id) {
        return new Promise(function (resolve, reject) {
            getWxAccessToken(wechat_id, function (token) {
                return resolve(token);
            })
        });
    }

    sleep(millisecond) {
        return new Promise(function (resolve, reject) {
            setTimeout(() => {
                resolve();
            }, millisecond);
        })
    }

}

export default new Wx();